use crate::error::*;
use std::sync::Arc;

use serde::{Deserialize, Serialize};

#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
pub enum HttpBackendFailedStatus {
    ConnError,
    ConnTimeout,
    RespError,
    RespTimeout,
    Http500,
    Http502,
    Http503,
    Http504,
}

#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct HttpProxyConf {
    // 哪些场景需要重试, 可选值:
    // conn_error, conn_timeout, resp_error, resp_timeout
    // http_500, http_502, http_503, http_504
    pub next_backend: Option<Vec<String>>,
    // 重试次数.
    pub next_backend_tries: Option<usize>,
}

pub struct HttpProxyRuntimeConf {
    pub next_backend: Vec<HttpBackendFailedStatus>,
    pub next_backend_tries: usize,
}

#[allow(dead_code)]
impl HttpProxyConf {
    pub fn to_runtime_conf(
        &self,
        pre_conf: Option<&Arc<HttpProxyRuntimeConf>>,
    ) -> Result<Arc<HttpProxyRuntimeConf>> {
        let def_next_backend;
        let def_next_backend_tries;

        match pre_conf {
            Some(pre_conf) => {
                def_next_backend = pre_conf.next_backend.clone();
                def_next_backend_tries = pre_conf.next_backend_tries;
            }

            None => {
                def_next_backend = Vec::new();
                def_next_backend_tries = 0;
            }
        }

        let mut next_backend = Vec::new();
        match &self.next_backend {
            Some(conf_next_backend) => {
                for v in conf_next_backend {
                    match v.as_str() {
                        "conn_error" => {
                            next_backend
                                .push(HttpBackendFailedStatus::ConnError);
                        }

                        "conn_timeout" => {
                            next_backend
                                .push(HttpBackendFailedStatus::ConnTimeout);
                        }

                        "resp_error" => {
                            next_backend
                                .push(HttpBackendFailedStatus::RespError);
                        }

                        "resp_timeout" => {
                            next_backend
                                .push(HttpBackendFailedStatus::RespTimeout);
                        }

                        "http_500" => {
                            next_backend.push(HttpBackendFailedStatus::Http500);
                        }

                        "http_502" => {
                            next_backend.push(HttpBackendFailedStatus::Http502);
                        }

                        "http_503" => {
                            next_backend.push(HttpBackendFailedStatus::Http503);
                        }

                        "http_504" => {
                            next_backend.push(HttpBackendFailedStatus::Http504);
                        }

                        _ => {
                            return Error::e_explain(
                                ErrorType::ParseError,
                                "invalid next_backend",
                            );
                        }
                    }
                }
            }
            None => {
                for v in def_next_backend {
                    next_backend.push(v);
                }
            }
        }

        let next_backend_tries =
            self.next_backend_tries.unwrap_or(def_next_backend_tries);

        let runtime_conf = HttpProxyRuntimeConf {
            next_backend,
            next_backend_tries,
        };

        Ok(Arc::new(runtime_conf.valid()?))
    }
}

impl HttpProxyRuntimeConf {
    fn valid(self) -> Result<Self> {
        Ok(self)
    }
}
